/**
 * This file is part of the CAKIS project.
 * 
 * Copyright (c) 2009-2010 - Florian Arndt, Christian Kerl
 * 
 * @license GNU Lesser General Public License v3 <http://www.opensource.org/licenses/lgpl-3.0.html>
 * @since   01.03.2010
 * @version SVN: $Id: AbstractRepository.java 97 2010-04-28 10:41:34Z christian-kerl@web.de $
 */

package org.cakis.server.model.repository.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.persistence.NoResultException;
import javax.persistence.Query;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.cakis.server.model.repository.Range;
import org.cakis.server.model.repository.Repository;

/**
 * Basic implementation of the {@link Repository} interface.
 * 
 * @author Christian Kerl
 */
public abstract class AbstractRepository<T extends Serializable> implements Repository<T> {
    private static final long serialVersionUID = 1L;

    @PersistenceContext
    protected EntityManager em;

    @Override
    public T findById(Long id) {
        T result = null;

        if(id != null) {
            result = em.find(getType(), id);
        }

        return result;
    }

    @Override
    public List<T> findRange(Range limit) {
        Query q = em.createQuery("select e from " + getType().getSimpleName() + " e");

        return list(limit(q, limit));
    }

    @Override
    public List<T> findAll() {
        return findRange(null);
    }

    @Override
    public T store(T object) {
        T result = object;

        if(getId(result) == null) {
            em.persist(object);
        } else if(!em.contains(object)) {
            result = em.merge(object);
        }

        em.flush();

        return result;
    }

    @Override
    public T delete(Long id) {
        return delete(findById(id));
    }

    @Override
    public T delete(T object) {
        if(object != null) {
            em.remove(object);
        }

        return object;
    }

    protected abstract Long getId(T object);

    @SuppressWarnings("unchecked")
    protected Class<T> getType() {
        ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass();
        return (Class<T>) superclass.getActualTypeArguments()[0];
    }

    /**
     * Sets the {@link Query#setFirstResult(int)} to {@link Range#Offset} and
     * the {@link Query#setMaxResults(int)} to {@link Range#Count}.
     */
    protected Query limit(Query q, Range limit) {
        if(limit != null) {
            q.setFirstResult(limit.Offset);
            q.setMaxResults(limit.Count);
        }
        return q;
    }

    /**
     * Type safe wrapper for {@link Query#getResultList()}.
     */
    @SuppressWarnings("unchecked")
    protected List<T> list(Query q) {
        return q.getResultList();
    }

    /**
     * Type and Exception safe wrapper for {@link Query#getSingleResult()}.
     */
    @SuppressWarnings("unchecked")
    protected T one(Query q) {
        try {
            return (T) q.getSingleResult();
        } catch(NoResultException e) {
            return null;
        }
    }
}
